package net.xiaoboli.mgp;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Gen {

    private Connection connection;

    private long code = 0;

    public Gen(Connection connection) {
        this.connection = connection;
        this.code = System.currentTimeMillis() / 1000;
    }

    public List<GenTableInfo> list() throws IOException, SQLException, TemplateException {
        List<GenTableInfo> list = new ArrayList<>();
        DatabaseMetaData metaData = this.connection.getMetaData();
        try (ResultSet rs = metaData.getTables(null, null, "%", new String[]{"TABLE", "VIEW"})) {
            while (rs.next()) {
                String tableName = rs.getString("TABLE_NAME");
                String remarks = rs.getString("REMARKS");
                list.add(new GenTableInfo(tableName, remarks));
            }
        }
        return list;
    }

    public GenData data(String tb) throws IOException, SQLException, TemplateException {
        Date now = new Date();
        GenData data = new GenData();

        //
        String modelName = PluginUtil.snakeToPascal(tb);
        String tablePath = tb.replace("_", "/");

        //
        data.setTime(now);
        data.setTimeStamp(now.getTime() / 1000);
        data.setModelName(modelName);
        data.setTableName(tb);
        data.setTablePath(tablePath);
        data.setFunId(tablePath.replace("/", "."));
        data.setCode(this.code++);

        //
        DatabaseMetaData metaData = this.connection.getMetaData();
        try (ResultSet rs = metaData.getTables(null, null, tb, new String[]{"TABLE", "VIEW"})) {
            while (rs.next()) {
                String remarks = rs.getString("REMARKS");
                data.setDescription(remarks);
            }
        }
        if (StringUtils.isBlank(data.getDescription())) {
            data.setDescription(modelName);
        }

        //
        int index = -1;
        List<ViewEntity> entities = new ArrayList<>();

        //
        try (ResultSet colRs = metaData.getColumns(null, null, tb, "%")) {
            while (colRs.next()) {
                String columnName = colRs.getString("COLUMN_NAME");
                String columnType = colRs.getString("TYPE_NAME");
                String columnRemarks = colRs.getString("REMARKS");
                //
                String defValue = colRs.getString("COLUMN_DEF");//默认值
                String isAutoincrement = colRs.getString("IS_AUTOINCREMENT");//是否自增
                //
                index++;
                ViewEntity entity = new ViewEntity();

                entity.setIndex(index);
                entity.setName(columnName);
                entity.setCamelName(PluginUtil.snakeToCamel(columnName));
                entity.setGetter("get" + PluginUtil.snakeToPascal(columnName));
                entity.setDataType(columnType);
                if (StringUtils.isBlank(columnRemarks))
                    entity.setRemark(columnName);
                else
                    entity.setRemark(columnRemarks.trim());

                //
                if ("YES".equals(isAutoincrement)) {
                    entity.setValue(null);
                } else if (StringUtils.isNotBlank(defValue)) {
                    entity.setValue(defValue);
                } else {
                    entity.setValue(null);
                }

                //
                entities.add(entity);

            }
        }

        //
        data.setColumns(entities);

        //
        return data;
    }

    public void generator(OutputStream outputStream, String tb, String view) throws IOException, SQLException, TemplateException {
        GenData data = this.data(tb);
        this.generator(outputStream, data, view);
    }

    private void generator(OutputStream outputStream, GenData data, String view) throws IOException, SQLException, TemplateException {
        Configuration cfg = new Configuration(new Version("2.3.23"));
        cfg.setClassLoaderForTemplateLoading(getClass().getClassLoader(), "mgp");
        cfg.setDefaultEncoding("UTF-8");
        Template template = cfg.getTemplate(view);
        try (StringWriter out = new StringWriter()) {
            template.process(data, out);
            out.flush();
            //
            outputStream.write(out.getBuffer().toString().getBytes(StandardCharsets.UTF_8));
        }
    }

    public void zip(OutputStream outputStream, String table, String[] views) throws IOException, SQLException, TemplateException {
        GenData data = this.data(table);
        String dirs = "";

        try (ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(8192)) {
            try (ZipOutputStream zos = new ZipOutputStream(arrayOutputStream)) {
                String tablePath = String.valueOf(data.getTablePath());
                String[] paths = tablePath.split("/");
                //
                for (int i = 0; i < paths.length; i++) {
                    dirs = dirs + paths[i] + "/";
                    zos.putNextEntry(new ZipEntry(dirs));
                }
                //
                for (String view : views) {
                    zos.putNextEntry(new ZipEntry(dirs + view));
                    generator(zos, data, view);
                }
                //
                zos.flush();
            }
            //
            arrayOutputStream.writeTo(outputStream);
        }

    }
}
